home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume1 / vnews / part6 < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  54.0 KB

  1. From: decvax!vax135!hou3c!ka
  2. Date: Mon, 21 Jan 85 22:39:03 est
  3. Newsgroups: mod.sources
  4. Subject: Vnews part 6
  5.  
  6. # Welcome to vnews release 2.11-B 1/17/85.
  7. # This is part 6 out of 7.
  8. # Feed me into sh (NOT csh).
  9.  
  10. if test ! -d postnews
  11. then    mkdir postnews
  12. fi
  13.  
  14. if test ! -d vnews
  15. then    mkdir vnews
  16. fi
  17.  
  18. cat > postnews/postnm5.c <<\!E!O!F!
  19. /*
  20.  * STRING SCANNING ROUTINES:
  21.  *    These routines act on a subject string which is pointed to by scanp.
  22.  *
  23.  * scchr    Skip over characters contained in string.
  24.  * scnchr    Skip over characters not in string.
  25.  * skipbl    Skip over spaces, tabs, and newlines.
  26.  * scomment    Skip to the end of a comment.
  27.  * scqnchr    Like scnchr, but skips over any char if inside quotes.
  28.  * skipsw    Skip over spaces, tabs, newlines, and comments.
  29.  * parseaddr    Parse an address, setting up pointers to various parts.
  30.  * getval    Return the characters between two pointers.
  31.  *
  32.  * Copyright 1984 by Kenneth Almquist.  All rights reserved.
  33.  *     Permission is granted for anyone to use and distribute, but not
  34.  *     sell, this program provided that this copyright notice is retained.
  35.  */
  36.  
  37. #include "postnm.h"
  38.  
  39. char *scanp;
  40.  
  41. scchr(chars)
  42.       char *chars;
  43.       {
  44.       int flag = 0;
  45.  
  46.       while (*scanp && index(chars, *scanp)) {
  47.             flag++;
  48.             scanp++;
  49.       }
  50.       return flag;
  51. }
  52.  
  53.  
  54. scnchr(chars)
  55.       char *chars;
  56.       {
  57.       int flag = 0;
  58.  
  59.       while (index(chars, *scanp) == NULL) {
  60.             flag++;
  61.             scanp++;
  62.       }
  63.       return flag;
  64. }
  65.  
  66.  
  67.  
  68. skipbl() {
  69.       register char c;
  70.  
  71.       while ((c = *scanp) == ' ' || c == '\t' || c == '\n')
  72.             scanp++;
  73. }
  74.  
  75.  
  76.  
  77. scomment() {
  78.       int nest;
  79.       register char *p;
  80.  
  81.       if (*(p = scanp) != '(')
  82.             return 0;
  83.       nest = 0;
  84.       for (;;) {
  85.             if (*p == '(')
  86.                   nest++;
  87.             else if (*p == ')') {
  88.                   if (--nest == 0)
  89.                         break;
  90.             } else {
  91.                   if (*p == '\\')
  92.                         p++;
  93.                   if (*p == '\0')
  94.                         jsynerr("Nonterminated comment");
  95.             }
  96.             p++;
  97.       }
  98.       scanp = p;
  99.       return 1;
  100. }
  101.  
  102.  
  103. scqnchr(chars)
  104.       char *chars;
  105.       {
  106.       register char *p = scanp;
  107.  
  108.       for (;;) {
  109.             if (*p == '"') {
  110.                   p++;
  111.                   while (*p != '"') {
  112.                         if (*p == '\\')
  113.                               p++;
  114.                         if (*p++ == '\0')
  115.                               jsynerr("Nonterminated string");
  116.                   }
  117.             } else {
  118.                   if (index(chars, *p))
  119.                         break;
  120.             }
  121.             p++;
  122.       }
  123.       if (p == scanp)
  124.             return 0;
  125.       else {
  126.             scanp = p;
  127.             return 1;
  128.       }
  129. }
  130.  
  131.  
  132.  
  133. skipws(chars)
  134.       char *chars;
  135.       {
  136.       for (;;) {
  137.             if (*scanp == '(')
  138.                   scomment();
  139.             else if (*scanp == '\0' || index(chars, *scanp) == NULL)
  140.                   break;
  141.             scanp++;
  142.       }
  143. }
  144.  
  145.  
  146.  
  147. /*
  148.  * Parse an address.
  149.  */
  150.  
  151. char *begreal, *endreal;        /* real name */
  152. char *begaddr, *endaddr;        /* machine address */
  153. char *beglocal, *endlocal;        /* local part */
  154. char *begdomain, *enddomain;        /* domain part */
  155.  
  156. parseaddr() {
  157.       char *startp = scanp;
  158.       char *p;
  159.  
  160.       begreal = begdomain = NULL;
  161.       if (*scanp == '.')
  162. syntax:     jsynerr("Illegal address syntax");
  163.       begaddr = beglocal = scanp;
  164.       if (*scanp == '<') {
  165. routeaddr:
  166.             scanp++;
  167.             skipbl();
  168.             begaddr = beglocal = scanp;
  169.             scqnchr(":>");
  170.             if (*scanp == ':') {
  171.                   scanp++;
  172.                   skipbl();
  173.                   beglocal = scanp;
  174.                   scqnchr(">");
  175.             }
  176.             if (*scanp != '>')
  177.                   goto syntax;
  178.             scanp = beglocal;
  179.             scqnchr("@>");
  180.             endlocal = scanp;
  181.             while (index(" \t\n", *(endlocal - 1)))
  182.                   endlocal--;
  183.             if (endlocal <= beglocal)
  184.                   goto syntax;
  185.             if (*scanp == '@') {
  186.                   scanp++;
  187.                   skipbl();
  188.                   begdomain = scanp;
  189.                   scqnchr(">");
  190.                   enddomain = scanp;
  191.                   while (index(" \t\n", *(enddomain - 1)))
  192.                         enddomain--;
  193.                   if (enddomain <= begdomain)
  194.                         goto syntax;
  195.             }
  196.             if (*scanp != '>')
  197.                   goto syntax;
  198.             scanp++;
  199.       } else {
  200.             scqnchr(" \t\n@(,<");
  201.             if (scanp == startp)
  202.                   jsynerr("Program bug");
  203.             endlocal = scanp;
  204.             skipbl();
  205.             if (*scanp == '@') {
  206.                   scanp++;
  207.                   skipbl();
  208.                   begdomain = scanp;
  209.                   if (scnchr(" \t\n(,<") == 0)
  210.                         goto syntax;
  211.                   enddomain = scanp;
  212.                   if (enddomain[-1] == '.')
  213.                         goto syntax;
  214.             } else {
  215.                   do p = scanp, skipbl();
  216.                      while (scqnchr(" \t\n@(,<"));
  217.                   if (*scanp != '<') {
  218.                         scanp = endlocal;
  219.                   } else {
  220.                         begreal = beglocal, endreal = p;
  221.                         goto routeaddr;
  222.                   }
  223.             }
  224.       }
  225.       if (begdomain)
  226.             endaddr = enddomain;
  227.       else
  228.             endaddr = endlocal;
  229.       if (begreal == NULL) {
  230.             p = scanp;
  231.             skipbl();
  232.             if (*scanp != '(') {
  233.                   scanp = p;
  234.             } else {
  235.                   begreal = scanp + 1;
  236.                   scomment();
  237.                   endreal = scanp;
  238.                   scanp++;        /* skip over ')' */
  239.             }
  240.       }
  241. }
  242.  
  243.  
  244.  
  245. char *
  246. getval(p, q, buf)
  247.       char *p, *q, *buf;
  248.       {
  249.  
  250.       if (buf == NULL)
  251.             buf = ckmalloc(q - p + 1);
  252.       bcopy(p, buf, q - p);
  253.       buf[q - p] = '\0';
  254.       return buf;
  255. }
  256. !E!O!F!
  257.  
  258. cat > postnews/postreply.c <<\!E!O!F!
  259. /*
  260.  * This program handles replies and followups for vnews.
  261.  * Synopsys:
  262.  *    reply -r file        # mail a reply
  263.  *    reply -f file        # generate a followup
  264.  */
  265.  
  266. #include <stdio.h>
  267. #include <sys/types.h>
  268. #include "config.h"
  269. #include "newsdefs.h"
  270. #include "libextern.h"
  271. #include "arthead.h"
  272. #include <sys/stat.h>
  273. #include <signal.h>
  274. #include <ctype.h>
  275.  
  276.  
  277. #define NOPOST 22
  278.  
  279. int followup;
  280. char *original;
  281. char *references;
  282. int vnews;
  283. struct arthead h;
  284. int checksum;
  285. int nchars;
  286. FILE *tfp;
  287. char tempfile[16];
  288. char tempout[16];
  289. int outfd;
  290.  
  291. FILE *ckfopen();
  292. char *savestr();
  293. int nopost();
  294.  
  295.  
  296.  
  297. main(argc, argv)
  298.     char **argv;
  299.     {
  300.     int c;
  301.     int pid;
  302.     extern char *optarg;
  303.  
  304.     signal(SIGQUIT, SIG_IGN);
  305.     while ((c = getopt(argc, argv, "r:f:v")) != EOF) {
  306.         switch (c) {
  307.         case 'r':
  308.             followup = 0;
  309.             original = optarg;
  310.             break;
  311.         case 'f':
  312.             followup = 1;
  313.             original = optarg;
  314.             break;
  315.         case 'v':
  316.             vnews = 1;
  317.             break;
  318.         }
  319.     }
  320.     if (original == NULL)
  321.         xerror("-f or -r must be specified");
  322.  
  323.     signal(SIGINT, nopost);
  324.  
  325.     pathinit();
  326.     getuser();
  327.  
  328.     writetemp(followup);
  329.  
  330.     edit();
  331.     if (samefile())
  332.         nopost();
  333.  
  334.     /* fork off a child and let parent terminate */
  335.     signal(SIGINT, SIG_IGN);
  336.     signal(SIGQUIT, SIG_IGN);
  337.     signal(SIGHUP, SIG_IGN);
  338. #ifdef SIGTSTP
  339.     signal(SIGTSTP, SIG_IGN);    /* I hope this works */
  340. #endif
  341.     if ((pid = fork()) == -1)
  342.         fprintf(stderr, "postreply: can't fork\n");
  343.     if (pid > 0)
  344.         _exit(0);
  345.  
  346.     nice(4);
  347.     sprintf(bfr, "%s.BAK", tempfile);
  348.     unlink(bfr);
  349.  
  350.     postit();
  351.     xxit(0);
  352. }
  353.  
  354.  
  355. /*
  356.  * Print a recorded message warning the poor luser what he is doing
  357.  * and demand that he understands it before proceeding.  Only do
  358.  * this for newsgroups listed in LIBDIR/recording.
  359.  */
  360. recording(ngrps)
  361. char *ngrps;
  362. {
  363.     char recbuf[BUFLEN];
  364.     FILE *fd;
  365.     char nglist[BUFLEN], fname[BUFLEN];
  366.     int  c, n, yes;
  367.  
  368.     sprintf(recbuf, "%s/recording", LIB);
  369.     fd = fopen(recbuf, "r");
  370.     if (fd == NULL)
  371.         return 0;
  372.     while ((fgets(recbuf, sizeof recbuf, fd)) != NULL) {
  373.         sscanf(recbuf, "%s %s", nglist, fname);
  374.         if (ngmatch(ngrps, nglist)) {
  375.             fclose(fd);
  376.             if (fname[0] == '/')
  377.                 strcpy(recbuf, fname);
  378.             else
  379.                 sprintf(recbuf, "%s/%s", LIB, fname);
  380.             fd = fopen(recbuf, "r");
  381.             if (fd == NULL)
  382.                 return 0;
  383.             while ((c = getc(fd)) != EOF)
  384.                 putc(c, stderr);
  385.             fclose(fd);
  386.             fprintf(stderr, "Do you understand this?  Hit <return> to proceed, <BREAK> to abort: ");
  387.             n = read(2, recbuf, BUFLEN);
  388.             c = recbuf[0];
  389.             yes = (c=='y' || c=='Y' || c=='\n' || c=='\n' || c==0);
  390.             if (n <= 0 || !yes)
  391.                 return -1;
  392.             else
  393.                 return 0;
  394.         }
  395.     }
  396.     fclose(fd);
  397.     return 0;
  398. }
  399.  
  400.  
  401.  
  402. /*
  403.  * Generate replies and followups.
  404.  */
  405.  
  406.  
  407. writetemp() {
  408.     FILE *artfp;
  409.     char subj[132];
  410.     char *p;
  411.     char *replyname();
  412.  
  413.     checksum = 0;
  414.     nchars = 0;
  415.     artfp = ckfopen(original, "r");
  416.     if (gethead(&h, artfp) == NULL)
  417.         xerror("Original article has garbled header");
  418.     fclose(artfp);
  419.  
  420.  
  421.     scopyn(h.h_title, subj, 128);
  422.     if (!prefix(subj, "Re:") && !prefix(subj, "re:")) {
  423.         strcpy(bfr, subj);
  424.         sprintf(subj, "Re: %s", bfr);
  425.     }
  426.  
  427.     p = h.h_nbuf;
  428.     if (hset(h.h_followto))
  429.         p = h.h_followto;
  430.     strcpy(bfr, p);
  431.     launder(bfr);
  432.     if (followup && recording(bfr))
  433.         nopost();
  434.  
  435.     gentemp(tempfile);
  436.     tfp = ckfopen(tempfile, "w");
  437.  
  438.     wstr("Command: %s\n", followup? "followup" : "reply");
  439.     wstr("Newsgroups: %s\n", bfr);
  440.     wstr("To: %s\n", replyname(&h, bfr));
  441.     wstr("Subject: %s\n", subj);
  442.     if (hset(h.h_keywords))
  443.         wstr("Keywords: %s\n", h.h_keywords);
  444.     wstr("Distribution: %s\n", hset(h.h_distribution)? h.h_distribution : "");
  445.     bfr[0] = 0;                /* References */
  446.     if (hset(h.h_references)) {
  447.         strcpy(bfr, h.h_references);
  448.         strcat(bfr, " ");
  449.     }
  450.     strcat(bfr, h.h_ident);
  451.     references = savestr(bfr);
  452.     wstr("References: %s\n", bfr);
  453.     wstr("\n");
  454.     fclose(tfp);
  455. }
  456.  
  457.  
  458.  
  459. wstr(fmt, a1, a2, a3, a4)
  460.     char *fmt;
  461.     {
  462.     char buf[1024];
  463.     register char *p;
  464.  
  465.     sprintf(buf, fmt, a1, a2, a3, a4);
  466.     for (p = buf ; *p ; ) {
  467.         checksum += *p++;
  468.         nchars++;
  469.     }
  470.     fputs(buf, tfp);
  471. }
  472.  
  473.  
  474.  
  475. /*
  476.  * Check to see if the newsgroup is moderated.  If so, the moderator's
  477.  * name is returned in moderator.
  478.  */
  479. #ifdef notdef
  480. check_mod(newsgroups, moderator)
  481. char *newsgroups;
  482. char *moderator;
  483. {
  484.     register FILE *fd;
  485.     char ng[64], mod[BUFLEN];
  486.  
  487.     sprintf(bfr, "%s/%s", LIB, "moderators");
  488.     if ((fd = fopen(bfr, "r")) == NULL)
  489.         return 0;
  490.     while (1) {
  491.         if (fgets(bfr, LBUFLEN, fd) == NULL) {
  492.             fclose(fd);
  493.             return 0;
  494.         }
  495.         twosplit(bfr, ng, mod);
  496.         if (ngmatch(newsgroups, ng)) {
  497.             strcpy(moderator, mod);
  498.             fclose(fd);
  499.             return 1;
  500.         }
  501.     }
  502. }
  503.  
  504.  
  505.  
  506. /*
  507.  * Split a line of the form
  508.  *        text whitespace text
  509.  * into two strings.    Also trim off any trailing newline.
  510.  * This is destructive on src.
  511.  */
  512. twosplit(src, dest1, dest2)
  513. char *src, *dest1, *dest2;
  514. {
  515.     register char *p;
  516.  
  517.     nstrip(src);
  518.     for (p=src; isalnum(*p) || ispunct(*p); p++)
  519.         ;
  520.     *p++ = 0;
  521.     for ( ; isspace(*p); p++)
  522.         ;
  523.     strcpy(dest1, src);
  524.     strcpy(dest2, p);
  525. }
  526. #endif
  527.  
  528.  
  529.  
  530. edit() {
  531.     char *p;
  532.     int pid;
  533.     int status;
  534.     char *arg[5];
  535.     register char **ap;
  536.     char *getenv(), *rindex();
  537.  
  538.     if ((arg[0] = getenv("EDITOR")) == NULL)
  539.         arg[0] = DFTEDITOR;
  540.     if ((p = rindex(arg[0], '/')) != NULL)
  541.         p++;
  542.     else
  543.         p = arg[0];
  544.     ap = &arg[1];
  545.     if (strcmp(p, "vi") == 0) {
  546.         *ap++ = "+";
  547.         *ap++ = tempfile;
  548.         *ap++ = original;
  549.     } else if (strcmp(p, "gmacs") == 0
  550.         || strcmp(p, "gemacs") == 0
  551.         || strcmp(p, "gem") == 0) {
  552.         *ap++ = original;
  553.         *ap++ = tempfile;
  554.     } else {
  555.         *ap++ = tempfile;
  556.         *ap++ = original;
  557.     }
  558.     *ap = NULL;
  559.  
  560.     if ((pid = fork()) == -1)
  561.         xerror("Can't fork");
  562.     if (pid == 0) {
  563.         signal(SIGQUIT, SIG_DFL);
  564.         execvp(arg[0], arg);
  565.         _exit(124);
  566.     }
  567.     signal(SIGINT, SIG_IGN);
  568.     status = pwait(pid);
  569.     if (status == 124 << 8)
  570.         xerror("Can't exec %s", arg[0]);
  571. }
  572.  
  573.  
  574.  
  575. samefile() {
  576.     struct stat statb;
  577.     register FILE *fp;
  578.     register int c;
  579.  
  580.     if (stat(tempfile, &statb) < 0)
  581.         xerror("%s has vanished!", tempfile);
  582.     if (statb.st_size == 0L)
  583.         return 1;
  584.     if (statb.st_size != nchars)
  585.         return 0;
  586.     fp = ckfopen(tempfile, "r");
  587.     while ((c = getc(fp)) != EOF)
  588.         checksum -= c;
  589.     return (checksum == 0);
  590. }
  591.  
  592.  
  593.  
  594. postit() {
  595.     int artfd;
  596.     int i;
  597.  
  598.     gentemp(tempout);
  599.     if ((outfd = creat(tempout, 0644)) < 0) {
  600.         fprintf(stderr, "\7\nCan't create %s\nreply failed\n\7", tempout);
  601.         xxit(1);
  602.     }
  603.     if (followup)
  604.         writestr("Subject: followup failed\n\n", outfd);
  605.     else
  606.         writestr("Subject: reply failed\n\n", outfd);
  607.  
  608.     if (runpost()) {
  609.         if ((artfd = open(tempfile, 0)) < 0)
  610.             writestr("postreply: can't reopen article\n", outfd);
  611.         else {
  612.             writestr("\nYour article follows:\n", outfd);
  613.             while ((i = read(artfd, bfr, LBUFLEN)) > 0)
  614.                 write(outfd, bfr, i);
  615.             close(artfd);
  616.         }
  617.         close(outfd);
  618.         unlink(tempfile);
  619.         close(0);
  620.         if (open(tempout, 0) != 0) {
  621.             fprintf(stderr, "\7Fatal error in reply!\nCan't reopen %s\7\n", tempout);
  622.             exit(1);
  623.         }
  624. #ifdef MAILER
  625.         execl(MAILER, MAILER, username, (char *)0);
  626. #endif
  627.         execl("/bin/mail", "mail", username, (char *)0);
  628.         fprintf(stderr, "\7Fatal error in reply!\nCan't exec mail program!\7\n");
  629.         exit(1);
  630.     }
  631. }
  632.  
  633.  
  634.  
  635. runpost() {
  636.     int pid;
  637.     int status;
  638.     int fildes[2];
  639.     FILE *fp;
  640.     int inewsfail;
  641.     static char prog[] = POSTNM;
  642.  
  643.     if (pipe(fildes) < 0) {
  644.         writestr("can't create pipe\n", outfd);
  645.         return -1;
  646.     }        
  647.     while ((pid = fork()) == -1)
  648.         sleep(10);
  649.     if (pid == 0) {
  650. #ifdef notdef
  651.         close(0);
  652.         if (open(tempfile, 0) != 0) {
  653.             sprintf(bfr, "postreply: can't open %s\n", tempfile);
  654.             writestr(bfr, outfd);
  655.             _exit(2);
  656.         }
  657. #endif
  658.         if (fildes[1] != 1) {
  659.             close(1);
  660.             dup(fildes[1]);
  661.             close(fildes[1]);
  662.         }
  663.         close(fildes[0]);
  664.         close(2);
  665.         dup(1);
  666.         execl(prog, prog, "-wiR", references, tempfile, (char *)0);
  667.         writestr("postreply: can't exec postnm\n", 1);
  668.         _exit(2);
  669.     }
  670.     close(fildes[1]);
  671.     fp = fdopen(fildes[0], "r");
  672.     inewsfail = 0;
  673.     while (fgets(bfr, LBUFLEN, fp) != NULL) {
  674.         writestr(bfr, outfd);
  675.         if (prefix(bfr, "inews:"))
  676.             inewsfail = 1;
  677.     }
  678.     status = pwait(pid);
  679.     if (status != 0 || inewsfail) {
  680.         if ((status & 0377) == 0)
  681.             sprintf(bfr, "Exit status %d from postnm\n", status >> 8);
  682.         else
  683.             sprintf(bfr, "postreply: postnm died with signal %d\n", status & 077);
  684.         writestr(bfr, outfd);
  685.         return -1;
  686.     }
  687.     return 0;
  688. }
  689.  
  690.  
  691. #ifdef notdef
  692. /*
  693.  * Save a copy of the article in the users NEWSARCHIVE file.
  694.  * The article is saved only if the user explicitly sets NEWSARCHIVE.
  695.  * Currently, we save both news and mail replies, which is
  696.  * rather questionable.
  697.  */
  698. save_article()
  699. {
  700.     register FILE *in, *out;
  701.     register int c;
  702.     time_t timenow, time();
  703.     char *today, *ctime();
  704.     char *ccname;
  705.  
  706.     if ((ccname = getenv("NEWSARCHIVE")) == NULL || ccname[0] == '\0')
  707.         return 0;
  708.     if ((in = fopen(tempfile, "r")) == NULL) {
  709.         writestr("Can't reopen temp file for article save\n", outfd);
  710.         return -1;
  711.     }
  712.     if ((out = fopen(ccname, "a")) == NULL) {
  713.         sprintf(bfr, "Can't open %s to save article\n", ccname);
  714.         writestr(bfr, outfd);
  715.         return -1;
  716.     }
  717.     timenow = time((time_t)0);
  718.     today = ctime(&timenow);
  719.     fprintf(out, "From postreply %s", today);
  720.     while ((c=getc(in)) != EOF)
  721.         putc(c, out);
  722.     putc('\n', out);
  723.     fclose(in);
  724.     fclose(out);
  725.     return 0;
  726. }
  727. #endif
  728.  
  729.  
  730. writestr(s, fd)
  731.     char *s;
  732.     int fd;
  733.     {
  734.     write(fd, s, strlen(s));
  735. }
  736.  
  737.  
  738. pwait(pid) {
  739.     int status;
  740.  
  741.     while (wait(&status) != pid);
  742.     return status;
  743. }
  744.  
  745.  
  746.  
  747. gentemp(s)
  748.     char s[16];
  749.     {
  750.     strcpy(s, "/tmp/repXXXXXX");
  751.     mktemp(s);
  752. }
  753.  
  754.  
  755.  
  756. xerror(fmt, a1, a2, a3, a4)
  757.     char *fmt;
  758.     {
  759.     int c;
  760.  
  761.     fputs("postreply: ", stderr);
  762.     fprintf(stderr, fmt, a1, a2, a3, a4);
  763.     fputs("\nContinue?", stderr);
  764.     while ((c = getchar()) != '\n' && c != EOF);
  765.     xxit(1);
  766. }
  767.  
  768.  
  769.  
  770. xxit(status) {
  771.     if (tempfile[0])
  772.         unlink(tempfile);
  773.     if (tempout[0])
  774.         unlink(tempout);
  775.     exit(status);
  776. }
  777.  
  778.  
  779.  
  780. nopost() {
  781.     xxit(NOPOST);
  782. }
  783. !E!O!F!
  784.  
  785. cat > vnews/2.11features <<\!E!O!F!
  786. + Erase and kill processing performed on count.
  787.  
  788. + Newsgroups presented in the order in which they appear in .newsrc.  This
  789.   allows newsgroup presentation order to be set on an individual basis.
  790.  
  791. + Articles are now presented grouped by discussion.  Within a discussion,
  792.   articles are sorted by date.
  793.  
  794. + An index page is now available.  The -A option will cause the index page
  795.   to be shown on entry to each group.  Scrolling off the end of the index
  796.   page using space gets you to the first article in the newsgroup.  You can
  797.   execute practically all vnews commands on the index page; the scrolling
  798.   commands scroll the index page and the other commands apply to the current
  799.   article.  The 'a' command will toggle you to/from the index page.
  800.  
  801. + There is now an article list stack.  The "N newsgroup" command will save
  802.   the current newsgroup on the stack so that when you reach the end of the
  803.   newsgroup all the articles in the newsgroup you just left will still be
  804.   there.  Currently, the "N newsgroup" command empties the stack before
  805.   beginning in order to keep things simple for the user.  The parent and
  806.   '<' commands create an article list containing one article.  Unless the
  807.   current article list contains a single article, the current article list
  808.   will be saved on the stack, so that you will automaticly return to where
  809.   you were.
  810.  
  811. + The "N newsgroup command" will now work even with unsubscribed groups.
  812. !E!O!F!
  813.  
  814. cat > vnews/ToDo <<\!E!O!F!
  815. Read in next newsgroups during idle time.  May not be able to store in
  816. memory, but could write record to temp file, and do sort when actually
  817. enter group.
  818.  
  819. Chdir to spool directory while running.
  820.  
  821. Add .rnlock checking.  (Use .newsrc.lck since rn use SIGEMT.)
  822.  
  823. Make N- get real previous group.
  824.  
  825. Make jd command save discussion for later.
  826.  
  827. Make t command get you to first unread article.
  828.  
  829. Make xerror do a longjmp to the code that resets the tty modes and updates
  830. the .newsrc file.  Save a copy of the .newsrc file in .newsrc.bak.
  831.  
  832. !E!O!F!
  833.  
  834. cat > vnews/artseq.c <<\!E!O!F!
  835. #include "vnews.h"
  836.  
  837. #ifdef SMALL            /* try to avoid running out of core */
  838. #define MAXNUMARTS    100    /* max arts we can read in one newsgroup */
  839. #endif
  840.  
  841. static struct svartlist alstack[10];    /* stack of article lists */
  842. struct svartlist *alptr = alstack;    /* stack pointer for above */
  843. static int readmode = NEXT;        /* currently unused */
  844. static struct ngentry *ngindex;        /* current location in .newsrc file */
  845.  
  846.  
  847. /*
  848.  * set or clear the unread article flag
  849.  */
  850. setnew(on) {
  851.     if (alptr->al_type != SVNEWSGROUP)
  852.         return;
  853.     if (on)
  854.         setunread(curart->i_artnum);
  855.     else
  856.         clrunread(curart->i_artnum);
  857. }
  858.  
  859.  
  860. /*
  861.  * Go to a particular article number
  862.  */
  863.  
  864. setartno(num)
  865. int num;
  866. {
  867.     if (num <= 0) {
  868.         msg("Bad article no.");
  869.         return;
  870.     }
  871.     if (num > numthisng) {
  872.         msg("Not that many articles.");
  873.         return;
  874.     }
  875.     curart = &thisng[num - 1];
  876.     setupart();
  877. }
  878.  
  879.  
  880. /*
  881.  * Advance the counter to the next unread article.
  882.  */
  883. nextart(count, zapthem) 
  884. {
  885.     if (count <= 0)
  886.         return FALSE;
  887.     while (--count >= 0) {
  888.         if (zapthem)
  889.             setnew(0);
  890.         if (readmode == SPEC || xflag) {
  891.             curart++, curind++;
  892.         } else {
  893.             while (++curart, ++curind <= numthisng && !isunread(curart->i_artnum))
  894.                 ;
  895.         }
  896.         if (curind > numthisng)
  897.             return getnxtng(FORWARD);
  898.     }
  899.     setupart();
  900.     return FALSE;
  901. }
  902.  
  903.  
  904. /*
  905.  * Vnews article list stack.  Pushal(type) pushes the current article list
  906.  * onto the stack.  Type is the type of the replacement article list.  Popal
  907.  * restores an article list from the stack.  Setupart should be called after
  908.  * popal.
  909.  */
  910.  
  911. struct alsave {
  912.     int a_numthisng;
  913.     int a_curind;
  914.     struct ngentry *a_curng;
  915.     int a_iwartlin;
  916.     int a_indexpg;
  917.     int a_maxartno;
  918. };
  919.  
  920.  
  921. pushal(type) {
  922.     struct alsave als;
  923.  
  924.     if (alptr >= alstack + 9) {
  925.         msg("newsgroups nested too deeply");
  926.         alptr->al_type = type;
  927.         return;
  928.     }
  929.     alptr->al_tfoffset = tfoffset;
  930.     als.a_numthisng = numthisng;
  931.     als.a_curind = curind;
  932.     als.a_curng = curng;
  933.     als.a_iwartlin = indexwin.w_artlin;
  934.     als.a_indexpg = indexpg;
  935.     als.a_maxartno = maxartno;
  936.     fseek(tfp, (long)tfoffset, 0);
  937.     fwrite((char *)&als, sizeof als, 1, tfp);
  938.     fwrite((char *)thisng, sizeof *thisng, numthisng, tfp);
  939.     tfoffset = ftell(tfp);
  940.     tflinno = -1;
  941.     (++alptr)->al_type = type;
  942. }
  943.  
  944.  
  945. popal() {
  946.     struct alsave als;
  947.  
  948.     tfoffset = (--alptr)->al_tfoffset;
  949.     fseek(tfp, (long)tfoffset, 0);
  950.     fread((char *)&als, sizeof als, 1, tfp);
  951.     numthisng = als.a_numthisng;
  952.     curind = als.a_curind;
  953.     indexwin.w_artlin = als.a_iwartlin;
  954.     indexpg = als.a_indexpg;
  955.     fread((char *)thisng, sizeof *thisng, numthisng, tfp);
  956.     indexwin.w_force = 2;
  957.     tflinno = -1;
  958.     if ((alptr+1)->al_type == SVNEWSGROUP)
  959.         setupgrp(als.a_curng, als.a_maxartno);
  960.     setartno(curind);
  961. }
  962.  
  963.  
  964.  
  965. switchng(ngp)        /* go to specific newsgroup */
  966.     struct ngentry *ngp;
  967.     {
  968.     if (alptr >= alstack + 8) {
  969.         msg("Nesting too deep.");
  970.         return;
  971.     }
  972.     if (alptr->al_type == SVARTICLE)
  973.         popal();
  974.     pushal(SVNEWSGROUP);
  975.     readinng(ngp, 1);
  976.     if (numthisng <= 0) {
  977.         popal();
  978.         msg("group? %s: empty", ngp->ng_name);
  979.     } else {
  980.         ngrp++;
  981.     }
  982.     setupart();
  983. }
  984.  
  985.  
  986. spclgrp(dp, a)
  987.     DPTR dp;
  988.     struct artrec *a;
  989.     {
  990.     if (alptr->al_type != SVARTICLE)
  991.         pushal(SVARTICLE);
  992.     scopyn(a->a_title, thisng[0].i_title, 36);
  993.     scopyn(a->a_from, thisng[0].i_from, 29);
  994.     thisng[0].i_nlines = a->a_nlines;
  995.     thisng[0].i_artnum = a->a_group[0].a_artno;
  996.     thisng[0].i_dptr = dp;
  997.     numthisng = 1;
  998.     curart = thisng;
  999.     setupart();
  1000.     sprintf(filename, "%s/%s", SPOOL, a->a_file);
  1001.     indexpg = 0;
  1002. }
  1003.  
  1004.  
  1005. getnxtng(direct) {
  1006.     if (direct == FORWARD && alptr > alstack) {    /* al stack stuff */
  1007.         popal();
  1008.         setupart();
  1009.         /* advance to next article if current one read */
  1010.         if ((alptr+1)->al_type == SVNEWSGROUP
  1011.          && isunread(curart->i_artnum) == 0)
  1012.             return nextart(1, 0);
  1013.         return FALSE;
  1014.     }
  1015.     while (alptr > alstack)        /* for backwards, clear al stack */
  1016.         popal();
  1017.  
  1018.     for (;;) {
  1019.         if (direct == FORWARD) {
  1020.             if ((ngindex = nextgrp(ngindex)) == NULL) {
  1021.                 quitflg++;
  1022.                 return TRUE;
  1023.             }
  1024.         } else {
  1025.             if ((ngindex = prevgrp(ngindex)) == NULL) {
  1026.                 msg("Can't back up.");
  1027.                 direct = FORWARD;
  1028.                 continue;
  1029.             }
  1030.         }
  1031.         if (ngindex->ng_unsub || !wewant(ngindex->ng_name))
  1032.             continue;
  1033.         readinng(ngindex, 0);
  1034.         if (numthisng > 0)
  1035.             break;
  1036.     }
  1037.     ngrp++;
  1038.     setupart();
  1039.     return FALSE;
  1040. }
  1041.  
  1042.  
  1043. setupart() {
  1044.     if (ngrp) {
  1045.         pngsize = maxartno;
  1046.         if (Aflag)
  1047.             indexpg = 1;
  1048.         if (indexpg)
  1049.             ngrp = 0;
  1050.     }
  1051.     curind = curart - thisng + 1;
  1052. #ifdef DEBUG
  1053.     fprintf(stderr, "getnextart settles on #%d, bit %d\n", curind, curart->i_artnum);
  1054. #endif
  1055.     dirname(curng->ng_name, filename);
  1056.     sprintf(filename + strlen(filename), "/%d", curart->i_artnum);
  1057.     if (fp != NULL) {
  1058.         fclose(fp);
  1059.         fp = NULL;
  1060.     }
  1061.     news = TRUE;
  1062.     indexwin.w_force |= 1;
  1063.     erased = 0;
  1064. }
  1065.  
  1066. /*
  1067.  * Read in the newsgroup.
  1068.  */
  1069. readinng(ngp, all)
  1070. struct ngentry *ngp;
  1071. {
  1072.     register struct artinfo *ip;
  1073.     struct artrec a, a2;
  1074.     DPTR dp, dp2;
  1075.     int ngnum = ng_num(ngp);
  1076.     ARTNO bit, oldbit;
  1077.     int first;
  1078.     int i;
  1079.     static int thisngsize = 20;
  1080.     int cmppart();
  1081.     char *ckmalloc(), *realloc();
  1082.  
  1083.     if (news) {
  1084.         curflag = CURHOME;
  1085. #ifdef CURSES
  1086.         move(0, 0);    /* let user know we are thinking */
  1087.         refresh();
  1088. #else
  1089.         _amove(0, 0);    /* let user know we are thinking */
  1090.         vflush();
  1091. #endif
  1092.     }
  1093.     if (thisng == NULL) {
  1094.         thisng = ckmalloc(20 * sizeof thisng[0]);
  1095.     }
  1096.     numthisng = 0;
  1097.     oldbit = 0;
  1098.     first = 1;
  1099.     BKWD_GROUP(ngnum, bit, dp, a) {
  1100.         if (first) {
  1101.             setupgrp(ngp, bit);
  1102.             first = 0;
  1103.         }
  1104.         while (--oldbit > bit)        /* clear intermediate bits */
  1105.             clrunread(oldbit);
  1106.         oldbit = bit;
  1107.         if (bit < minartno && !all)
  1108.             break;
  1109.         if (!(all || isunread(bit)))
  1110.             continue;
  1111.         /* later:  rethink the handling of all */
  1112.         if ((a.a_flags & A_NOFILE) || !aselect(&a, 0) || haveunsub(dp)) {
  1113. skipit:            clrunread(bit);
  1114.             continue;
  1115.         }
  1116.         a2.a_subtime = a.a_subtime;
  1117.         for (dp2 = a.a_parent, i = 0 ; dp2 != DNULL && ++i < 100 ; dp2 = a2.a_parent) {
  1118.             if (haveunsub(dp2))
  1119.                 goto skipit;
  1120.             readrec(dp2, &a2);
  1121.         }
  1122.         if (i == 100)
  1123.             fprintf(stderr, "Parent loop %s %s \r\n", a.a_ident, a2.a_ident);    /*DEBUG*/
  1124.         if (numthisng >= thisngsize) {
  1125.             thisngsize += 4;
  1126.             if ((thisng = realloc((char *)thisng, thisngsize * sizeof thisng[0])) == NULL)
  1127.                 xerror("Out of space");
  1128.         }
  1129.         ip = &thisng[numthisng];
  1130.         ip->i_artnum = bit;
  1131.         ip->i_subtime = a.a_subtime;
  1132.         ip->i_basetime = a2.a_subtime;
  1133.         scopyn(a.a_title, ip->i_title, 37);
  1134.         scopyn(a.a_from, ip->i_from, 29);
  1135.         ip->i_dptr = dp;
  1136.         ip->i_nlines = a.a_nlines;
  1137. #ifndef DEBUG
  1138.         if (debugging > 1)
  1139. #endif
  1140.             fprintf(stderr, "storing %d in %d, subtime %d\n", bit, numthisng, ip->i_subtime);
  1141.         numthisng++;
  1142. #ifdef MAXNUMARTS
  1143.         if (numthisng >= MAXNUMARTS) {
  1144.             printf("Warning - more than %d new articles, missing some.\n", MAXNUMARTS);
  1145.             goto nomore;        /* exit loop */
  1146.         }
  1147. #endif
  1148.     }
  1149.     while (--oldbit >= minartno)
  1150.         clrunread(oldbit);
  1151. nomore:
  1152.     if (numthisng > 0) {
  1153.         qsort((char *)thisng, numthisng, sizeof thisng[0], cmppart);
  1154.     }
  1155.     curart = thisng;        /* go to start of group */
  1156.     indexwin.w_artlin = 0;
  1157.     indexwin.w_force = 2;
  1158. }
  1159.  
  1160.  
  1161. /*
  1162.  * Check whether we ahve unsubscribed to the discussion.
  1163.  */
  1164. haveunsub(dp)
  1165.     DPTR dp;
  1166.     {
  1167.     register DPTR *p;
  1168.     register int i;
  1169.  
  1170.     for (i = ndunsub, p = dunsub ; --i >= 0 ; p++)
  1171.         if (*p == dp)
  1172.             return 1;
  1173.     return 0;
  1174. }
  1175.  
  1176.  
  1177. /*
  1178.  * Compare two articles, and determine if they are in the same discussion.
  1179.  * If not, we sort by time of base article.
  1180.  */
  1181. cmppart(n1, n2)
  1182. register struct artinfo *n1, *n2;
  1183. {
  1184.     if (n1->i_basetime > n2->i_basetime)
  1185.         return 1;            /* different discussions */
  1186.     else if (n1->i_basetime < n2->i_basetime)
  1187.         return -1;            /* different discussions */
  1188.     else if (n1->i_subtime > n2->i_subtime)
  1189.         return 1;            /* same discussion */
  1190.     else if (n1->i_subtime < n2->i_subtime)
  1191.         return -1;            /* same discussion */
  1192.     else
  1193.         return 0;            /* same discussion */
  1194. }
  1195.  
  1196.  
  1197.  
  1198. /*
  1199.  * Return TRUE if the user has not ruled out this article.
  1200.  */
  1201. aselect(a, insist)
  1202. register struct artrec *a;
  1203. int    insist;
  1204. {
  1205.     extern char *titles[];
  1206.  
  1207.     if (insist)
  1208.         return TRUE;
  1209.     if (tflag && !titmat(a->a_title, titles))
  1210.         return FALSE;
  1211.     if (aflag && a->a_rectime < atime)
  1212.         return FALSE;
  1213.     if (a->a_ngroups > 1 && !rightgroup(a))
  1214.         return FALSE;
  1215.     if (fflag && a->a_parent != DNULL)
  1216.         return FALSE;
  1217.     return TRUE;
  1218. }
  1219.  
  1220.  
  1221.  
  1222. /*
  1223.  * Code to avoid showing multiple articles for vnews.
  1224.  * Works even if you exit vnews.
  1225.  * Returns nonzero if we should show this article.
  1226.  */
  1227. rightgroup(a)
  1228.     struct artrec *a;
  1229.     {
  1230.     int i, flag;
  1231.     struct ngentry *ngp;
  1232.  
  1233.     flag = 1;
  1234.     for (i = 0 ; i < a->a_ngroups ; i++) {
  1235.         ngp = ngtable + a->a_group[i].a_ngnum;
  1236.         if (ngp == curng)
  1237.             return flag;
  1238.         if (wewant(ngp->ng_name) && ngp->ng_unsub == 0) {
  1239.             flag = 0;
  1240.         }
  1241.     }
  1242.     /* "Can't happen" */
  1243.     return 1;
  1244. }
  1245.  
  1246.  
  1247.  
  1248. /*
  1249.  * Return true if the newsgroup was specified in the -n option.
  1250.  */
  1251.  
  1252. wewant(name)
  1253.     char *name;
  1254.     {
  1255.     return ngmatch(name, sublist);
  1256. }
  1257. !E!O!F!
  1258.  
  1259. cat > vnews/curterm.c <<\!E!O!F!
  1260. /*
  1261.  * Additions to curses.
  1262.  */
  1263.  
  1264. #include <stdio.h>
  1265. #include <curses.h>
  1266.  
  1267. /*
  1268.  * move to the bottom of the screen.
  1269.  */
  1270.  
  1271. botscreen() {
  1272.     move(LINES-1, 0);
  1273.     refresh();
  1274.     putchar('\n');
  1275.     fflush(stdout);
  1276. }
  1277.  
  1278.  
  1279. /*
  1280.  * Clear a line.
  1281.  */
  1282.  
  1283. clrline(linno) {
  1284.     move(linno, 0);
  1285.     clrtoeol();
  1286. }
  1287.  
  1288.  
  1289. #ifdef ACURSES
  1290. /*
  1291.  * Ring the bell on the terminal.
  1292.  */
  1293.  
  1294. beep() {
  1295.     putchar('\007');
  1296.     fflush(stdout);
  1297. }
  1298. #endif
  1299. !E!O!F!
  1300.  
  1301. cat > vnews/dispcntl.c <<\!E!O!F!
  1302. #include "vnews.h"
  1303.  
  1304. #ifdef STATTOP
  1305. #define PRLINE    0    /* prompter line */
  1306. #define SPLINE    1    /* secondary prompt line */
  1307. #define ARTWIN    2    /* first line of article window */
  1308. #else
  1309. #define PRLINE    (ROWS-1)/* prompter line */
  1310. #define SPLINE    (ROWS-2)/* secondary prompt line */
  1311. #define ARTWIN    0    /* first line of article window */
  1312. #endif
  1313.  
  1314. int dumpart(), dumpheaders(), dumphelp(), dumphdr();
  1315. int nullsub();
  1316.  
  1317. struct window artwin = {        /* window containing article */
  1318.     0, 0, dumpart, 2};
  1319. struct window indexwin = {        /* window containing article index */
  1320.     0, 0, dumpheaders, 2};
  1321. struct window helpwin = {        /* window containing help message */
  1322.     0, 0, dumphelp, 2};
  1323. struct window emptywin = {        /* empty window */
  1324.     0, 0, nullsub, 2};
  1325. struct window hdrwin = {        /* window containing header */
  1326.     0, 0, dumphdr, 2};
  1327. extern struct svartlist *alptr;        /* article list stack */
  1328.  
  1329.  
  1330.  
  1331. /*
  1332.  * Open the current article.  Returns nonzero on failure.
  1333.  */
  1334. openart() {
  1335.     struct stat statb;
  1336.     FILE *gethead();
  1337.  
  1338.     if (fp != NULL)
  1339.         return 0;
  1340. #ifdef CURSES
  1341.     move(0, 0);    /* let user know we are thinking */
  1342.     refresh();
  1343. #else
  1344.     _amove(0, 0);    /* let user know we are thinking */
  1345.     vflush();
  1346. #endif
  1347.     artwin.w_force = 2;
  1348.     lastlin = 0;
  1349.     fp = fopen(filename, "r");
  1350.     if (fp == NULL) {
  1351.         msg("Can't open %s", filename);
  1352.         artlines = 0;
  1353.         artread = 1;
  1354.         return TRUE;
  1355.     }
  1356.     fstat(fileno(fp), &statb);
  1357.     artlength = statb.st_size;
  1358.     if (gethead(&h, fp) == NULL) {
  1359.         msg("Garbled header");
  1360.         fclose(fp);
  1361.         fp = NULL;
  1362.         artlines = 0;
  1363.         artread = 1;
  1364.         return TRUE;
  1365.     }
  1366.     {    /* strip off any notesfile header */
  1367.         register c;
  1368.         register char *p = h.h_title + strlen(h.h_title) - 5;
  1369.         if (p > h.h_title
  1370.          && (strcmp(p, " (nf)") == 0 || strcmp(p, "(nf)\"") == 0)) {
  1371.             if ((c = getc(fp)) != '#') {
  1372.                 ungetc(c, fp);
  1373.             } else {
  1374.                 while ((c = getc(fp)) != '\n' && c != EOF);
  1375.                 while ((c = getc(fp)) != '\n' && c != EOF);
  1376.                 while ((c = getc(fp)) != '\n' && c != EOF);
  1377.             }
  1378.         }
  1379.     }
  1380.     artbody = ftell(fp);
  1381.     fmthdr();
  1382.     artlines = lastlin;
  1383.     artread = 0;
  1384.     if (! cflag && hdrend < ARTWLEN)
  1385.         hdronly = 1;
  1386.     artwin.w_artlin = 0;
  1387.     return 0;
  1388. }
  1389.  
  1390.  
  1391.  
  1392. /*
  1393.  * Print out whatever the appropriate header is
  1394.  */
  1395. fmthdr() {
  1396.     char *vbriefdate();
  1397.     char date[64];
  1398.  
  1399.     if (ngrp) {
  1400.         ngrp = 0;
  1401.         if (!hflag) {
  1402.             sprintf(bfr, "Newsgroup %s", curng->ng_name);  tfappend(bfr);
  1403.             tfappend("");
  1404.         }
  1405.     }
  1406.     hdrstart = lastlin;
  1407.     if (!hflag) {
  1408.         sprintf(bfr, "Article %s %s",
  1409.             h.h_ident, vbriefdate(h.h_subdate, date));
  1410.         tfappend(bfr);
  1411.     }
  1412.     vhprint(&h, pflag ? 1 : 0);
  1413.     sprintf(bfr, "(%d lines)", curart->i_nlines); tfappend(bfr);
  1414.     tfappend("");
  1415.     hdrend = lastlin;
  1416. }
  1417.  
  1418.  
  1419.  
  1420. /* Arpa format: Sat, 14-May-83 03:45:14 EDT */
  1421. /* Bugs: doesn't work on article with non-arpa dates */
  1422. char *
  1423. vbriefdate(q, date)
  1424.     register char *q;
  1425.     char *date;
  1426.     {
  1427.     register char *p;
  1428.     register i;
  1429.     char day[2];
  1430.  
  1431.     p = date;
  1432.     if (q[3] == ',') {
  1433.         for (i = 3 ; --i >= 0 ; )
  1434.             *p++ = *q++;
  1435.         *p++ = ' ';
  1436.         q += 2;
  1437.     }
  1438.     day[0] = *q++;
  1439.     if (*q != ' ' && *q != '-') day[1] = *q++;
  1440.     else day[1] = '\0';
  1441.     q++;
  1442.     for (i = 3 ; --i >= 0 ; )
  1443.         *p++ = *q++;
  1444.     *p++ = ' ';
  1445.     *p++ = day[0];
  1446.     if (day[1]) *p++ = day[1];
  1447.     q += 5;
  1448.     *p++ = ' ';
  1449.     if (q[-1] != '0') *p++ = q[-1];
  1450.     for (i = 4 ; --i >= 0 ; )
  1451.         *p++ = *q++;
  1452.     *p++ = '\0';
  1453.     return date;
  1454. }        
  1455.  
  1456. /*
  1457.  * Print the file header to the temp file.
  1458.  */
  1459. vhprint(hp, verbose)
  1460. register struct arthead *hp;
  1461. int    verbose;
  1462. {
  1463.     register char    *p1, *p2;
  1464.     int    i;
  1465.     char    fname[BUFLEN];
  1466.  
  1467.     fname[0] = '\0';        /* init name holder */
  1468.  
  1469.     p1 = index(hp->h_from, '(');    /* Find the sender's full name. */
  1470.     if (p1 == NULL && hset(hp->h_path))
  1471.         p1 = index(hp->h_path, '(');
  1472.     if (p1 != NULL) {
  1473.         strcpy(fname, p1+1);
  1474.         p2 = index(fname, ')');
  1475.         if (p2 != NULL)
  1476.             *p2 = '\0';
  1477.     }
  1478.  
  1479.     sprintf(bfr, "Subject: %s", hp->h_title);
  1480.     if ((i = strlen(bfr) - 7) > 9
  1481.      && strcmp(bfr + i, " - (nf)") == 0
  1482.      && (strncmp(bfr+9, "Re: ", 4) != 0 || i < 9+39))
  1483.         bfr[i] = '\0';        /* clobber "- (nf)" */
  1484.     tfappend(bfr);
  1485.     if (!hflag && hset(hp->h_keywords))
  1486.         sprintf(bfr, "Keywords: %s", hp->h_keywords), tfappend(bfr);
  1487.     if (verbose) {
  1488.         sprintf(bfr, "From: %s", hp->h_from); tfappend(bfr);
  1489.         sprintf(bfr, "Path: %s", hp->h_path); tfappend(bfr);
  1490.         if (hset(hp->h_organization))
  1491.             sprintf(bfr, "Organization: %s", hp->h_organization), tfappend(bfr);
  1492.     }
  1493.     else {
  1494.         if (p1 != NULL)
  1495.             *--p1 = '\0';        /* bump over the '(' */
  1496. #ifdef INTERNET
  1497.         /*
  1498.          * Prefer Path line if it's in internet format, or if we don't
  1499.          * understand internet format here, or if there is no reply-to.
  1500.          */
  1501.         sprintf(bfr, "From: %s", hp->h_from);
  1502. #else
  1503.         sprintf(bfr, "Path: %s", tailpath(hp));
  1504. #endif
  1505.         if (fname[0] != '\0') {
  1506.             strcat(bfr, " (");
  1507.             strcat(bfr, fname);
  1508.             if (hset(hp->h_organization) && !hflag) {
  1509.                 strcat(bfr, " @ ");
  1510.                 strcat(bfr, hp->h_organization);
  1511.             }
  1512.             strcat(bfr, ")");
  1513.         }
  1514.         tfappend(bfr);
  1515.         if (p1 != NULL)
  1516.             *p1 = ' ';
  1517.         if (hset(hp->h_ctlmsg)) {
  1518.             sprintf(bfr, "Control: %s", hp->h_ctlmsg);
  1519.             tfappend(bfr);
  1520.         }
  1521.     }
  1522.  
  1523.     if (verbose) {
  1524.         sprintf(bfr, "Newsgroups: %s", hp->h_nbuf); tfappend(bfr);
  1525.         sprintf(bfr, "Date: %s", hp->h_subdate); tfappend(bfr);
  1526.         if (hset(hp->h_sender))
  1527.             sprintf(bfr, "Sender: %s", hp->h_sender), tfappend(bfr);
  1528.         if (hset(hp->h_replyto))
  1529.             sprintf(bfr, "Reply-To: %s", hp->h_replyto), tfappend(bfr);
  1530.         if (hset(hp->h_followto))
  1531.             sprintf(bfr, "Followup-To: %s", hp->h_followto), tfappend(bfr);
  1532.     }
  1533.     else if (strcmp(hp->h_nbuf, curng->ng_name) != 0)
  1534.         sprintf(bfr, "Newsgroups: %s", hp->h_nbuf), tfappend(bfr);
  1535. }
  1536.  
  1537. /*
  1538.  * Append file to temp file, handling control characters, folding lines, etc.
  1539.  * We don't grow the temp file to more than nlines so that a user won't have
  1540.  * to wait for 20 seconds to read in a monster file from net.sources.
  1541.  * What we really want is coroutines--any year now.
  1542.  */
  1543.  
  1544. #define ULINE 0200
  1545. static char *maxcol;
  1546.  
  1547.  
  1548. appfile(iop, nlines)
  1549.     register FILE *iop;
  1550.     {
  1551.     register int c;
  1552.     register char *icol;    /* &bfr[0] <= icol <= maxcol */
  1553.  
  1554.     if (artread || artlines >= nlines)
  1555.         return;
  1556.     maxcol = bfr;
  1557.     icol = bfr;
  1558.     while ((c = getc(iop)) != EOF) {
  1559.         switch (c) {
  1560.         case ' ':
  1561.             if (icol == maxcol && icol < bfr + LBUFLEN - 1) {
  1562.                 *icol++ = ' ';
  1563.                 maxcol = icol;
  1564.             } else {
  1565.                 icol++;
  1566.             }
  1567.             break;
  1568.         case '\t':
  1569.             icol = (icol - bfr &~ 07) + 8 + bfr;
  1570.             growline(icol);
  1571.             break;
  1572.         case '\b':
  1573.             if (icol > bfr) --icol;
  1574.             break;
  1575.         case '\n':
  1576.             outline();
  1577.             if (artlines >= nlines)
  1578.                 return;
  1579.             icol = bfr;
  1580.             break;
  1581.         case '\r':
  1582.             icol = bfr;
  1583.             break;
  1584.         case '\f':
  1585.             outline(); outline(); outline();
  1586.             if (artlines >= nlines)
  1587.                 return;
  1588.             icol = bfr;
  1589.             break;
  1590.         default:
  1591.             if (c < ' ' || c > '~')
  1592.                 break;
  1593.             else if (icol >= bfr + LBUFLEN - 1)
  1594.                 icol++;
  1595.             else if (icol == maxcol) {
  1596.                 *icol++ = c;
  1597.                 maxcol = icol; }
  1598.             else if (*icol == ' ')
  1599.                 *icol++ = c;
  1600.             else if (c == '_')
  1601.                 *icol++ |= ULINE;
  1602.             else
  1603.                 *icol++ = (c | ULINE);
  1604.             break;
  1605.         }
  1606.     }
  1607.     if (maxcol != bfr)        /* file not terminated with newline */
  1608.         outline();
  1609.     artread++;
  1610. }
  1611.  
  1612.  
  1613.  
  1614. growline(col)
  1615.     char *col;
  1616.     {
  1617.     while (maxcol < col && maxcol < bfr + LBUFLEN - 1)
  1618.         *maxcol++ = ' ';
  1619. }
  1620.  
  1621.  
  1622. outline() {
  1623.     *maxcol = '\0';
  1624.     if (strncmp(bfr, ">From ", 6) == 0) {
  1625.         register char *p;
  1626.         for (p = bfr ; (*p = p[1]) != '\0' ; p++);
  1627.     }
  1628.     tfappend(bfr);
  1629.     if (maxcol > bfr)
  1630.         artlines = lastlin;
  1631.     maxcol = bfr;
  1632. }
  1633.  
  1634.  
  1635.  
  1636. prget(prompter, buf)
  1637.     char *prompter, *buf;
  1638.     {
  1639.     char *p, *q, *r;
  1640.     int c, lastc;
  1641.  
  1642.     curflag = CURP2;
  1643.     r = buf;
  1644.     lastc = '\0';
  1645.     for (;;) {
  1646.         *r = '\0';
  1647.         p = secpr;
  1648.         for (q = prompter ; *q ; q++)
  1649.             *p++ = *q;
  1650.         for (q = buf ; *q ; q++) {
  1651.             if (p < &secpr[SECPRLEN-1] && *q >= ' ' && *p <= '~')
  1652.                 *p++ = *q;
  1653.         }
  1654.         *p = '\0';
  1655.         c = vgetc();
  1656.         if (c == '\n' || c == INTR) {
  1657.             break;
  1658.         }
  1659.         if (c == cerase) {
  1660.             if (lastc == '\\')
  1661.                 r[-1] = c;
  1662.             else if (r > buf)
  1663.                 r--;
  1664.         } else if (c == ckill) {
  1665.             if (lastc == '\\')
  1666.                 r[-1] = c;
  1667.             else
  1668.                 r = buf;
  1669.         } else if (r < buf + SECPRLEN - 1) {
  1670.             *r++ = c;
  1671.         } else {
  1672.             beep();
  1673.         }
  1674.         lastc = c;
  1675.     }
  1676.     curflag = CURHOME;
  1677.     secpr[0] = '\0';
  1678.     if (c == INTR) {
  1679.         nextwin = curwin;
  1680.         return 1;
  1681.     } else
  1682.         return 0;
  1683. }
  1684.  
  1685.  
  1686.  
  1687. /*** Routines for handling temporary file ***/
  1688.  
  1689. /*
  1690.  * Append to temp file.
  1691.  * Long lines are folded.
  1692.  */
  1693.  
  1694. tfappend(line)
  1695.     char *line;
  1696.     {
  1697.     while (strlen(line) > COLS) {
  1698.         tfput(line, lastlin++);
  1699.         line += COLS;
  1700.     }
  1701.     tfput(line, lastlin++);
  1702. }
  1703.  
  1704.  
  1705. tfput(line, linno)
  1706.     char *line;
  1707.     {
  1708.     register char *p;
  1709.     register FILE *rtfp;        /* try to make it a litte faster */
  1710.     register int i;
  1711.  
  1712.     p = line;
  1713. #if BSDREL > 7
  1714.     i = (COLS + 1) &~ 1;
  1715. #else
  1716.     i = COLS;
  1717. #endif
  1718.     tfseek(linno, 1);
  1719.     rtfp = tfp;
  1720.     while (--i >= 0) {
  1721.         if (*p)
  1722.             putc(*p++, rtfp);
  1723.         else
  1724.             putc('\0', rtfp);
  1725.     }
  1726.     tflinno++;
  1727. }
  1728.  
  1729.  
  1730. tfget(line, linno)
  1731.     char *line;
  1732.     {
  1733.     tfseek(linno, 0);
  1734. #if BSDREL > 7
  1735.     fread(line, (COLS + 1) &~ 1, 1, tfp);
  1736. #else
  1737.     fread(line, COLS, 1, tfp);
  1738. #endif
  1739.     line[COLS] = '\0';
  1740.     tflinno++;
  1741. }
  1742.  
  1743.  
  1744. tfseek(linno, wrflag) {
  1745.     static int last = 1;
  1746.  
  1747.     if (linno != tflinno || wrflag != last) {
  1748. #if BSDREL > 7
  1749.         fseek(tfp, (long)linno * (COLS + 1 &~ 1) + tfoffset, 0);
  1750. #else
  1751.         fseek(tfp, (long)linno * COLS + tfoffset, 0);
  1752. #endif
  1753.         tflinno = linno;
  1754.         last = wrflag;
  1755.     }
  1756. }
  1757.  
  1758.  
  1759.  
  1760. /*** display management ***/
  1761.  
  1762.  
  1763. msg(s, a1, a2, a3, a4, a5, a6) char *s; {
  1764.     sprintf(secpr, s, a1, a2, a3, a4, a5, a6);
  1765. }
  1766.  
  1767.  
  1768. scroll(amount) {
  1769.     register struct window *w;
  1770.  
  1771.     w = &artwin;
  1772.     if (indexpg)
  1773.         w = &indexwin;
  1774.     if ((w->w_artlin += amount) < 0)
  1775.         w->w_artlin = 0;
  1776. }
  1777.  
  1778.  
  1779.  
  1780. /*
  1781.  * Update the display.
  1782.  * The display is entirely controlled by this routine,
  1783.  * which means that this routine may get pretty snarled.
  1784.  */
  1785.  
  1786. static struct window *savewin;        /* window on last call to updscr */
  1787.  
  1788. updscr() {
  1789.     int i;
  1790.     register struct window *w = curwin;
  1791.     char buf[40];
  1792.  
  1793.     if (checkin())
  1794.         return;
  1795.     if (w != savewin)
  1796.         w->w_force = 2;
  1797.     if ((w->w_force || w->w_artlin != w->w_svartlin)
  1798.      && (quitflg == 0 || w == &emptywin)) {
  1799. #ifndef CURSES
  1800.         if (w->w_force < 2)
  1801.             ushift(ARTWIN, ARTWIN+ARTWLEN-1, w->w_artlin - w->w_svartlin);
  1802. #endif
  1803.         for (i = ARTWIN ; i < ARTWIN + ARTWLEN ; i++)
  1804.             clrline(i);
  1805.         (*w->w_dump)(w->w_artlin, ARTWIN, ARTWLEN);
  1806.         w->w_force = 0;
  1807.         w->w_svartlin = w->w_artlin;
  1808.         savewin = w;
  1809.     }
  1810.     clrline(SPLINE), clrline(PRLINE);
  1811. #ifdef STATTOP
  1812.     mvaddstr(PRLINE, 0, prompt);
  1813. #else
  1814.     if (strlen(secpr) <= COLS)
  1815.         mvaddstr(PRLINE, 0, prompt);
  1816. #endif
  1817.     mvaddstr(PRLINE, 48, timestr);
  1818.     if (alptr->al_type == SVARTICLE)
  1819.         sprintf(buf, "%.18s", curng->ng_name);
  1820.     else
  1821.         sprintf(buf, "%.18s %d/%d ", curng->ng_name, curind, numthisng);
  1822.     mvaddstr(PRLINE, 20, buf);
  1823.     if (ismail)
  1824.         mvaddstr(PRLINE, 62, ismail > 1? "MAIL" : "mail");
  1825.     mvaddstr(SPLINE, 0, secpr);
  1826.     if (curflag == CURP1)
  1827.         move(PRLINE, strlen(prompt));
  1828.     else if (curflag == CURHOME)
  1829.         move(0, 0);
  1830.     refresh();
  1831. }
  1832.  
  1833.  
  1834. addnum(n) {
  1835.     if (n >= 10)
  1836.         addnum(n / 10);
  1837.     addch(n % 10 + '0');
  1838. }
  1839.  
  1840.  
  1841.  
  1842. /*
  1843.  * Dump the article to the screen.  Updscr has already cleared the region.
  1844.  */
  1845.  
  1846. dumpart(artlin, scrlin, count) {
  1847.     register int i;
  1848.  
  1849.     if (hdronly && count > hdrend - artlin)
  1850.         count = hdrend - artlin;
  1851. #ifdef DIGPAGE
  1852.     if (endsuba > 0 && count > endsuba - artlin)
  1853.         count = endsuba - artlin;
  1854. #endif
  1855.     if (count > lastlin - artlin)
  1856.         count = lastlin - artlin;
  1857.     for (i = 0 ; i < count ; i++) {
  1858.         tfget(bfr, artlin + i);
  1859.         mvaddstr(scrlin + i, 0, bfr);
  1860.     }
  1861. }
  1862.  
  1863.  
  1864. /*
  1865.  * All headers command: show headers for this group.
  1866.  */
  1867. dumpheaders(artlin, scrlin, count)
  1868. {
  1869.     register int i;
  1870.  
  1871.     for (i = count ; --i >= 0 ; artlin++, scrlin++) {
  1872.         if (artlin == 0) {
  1873.             sprintf(bfr, " IND FILE LINES Newsgroup %s, %d articles", curng->ng_name, numthisng);
  1874.             mvaddstr(scrlin, 0, bfr);
  1875.         } else if (artlin >= 2 && artlin < numthisng + 2) {
  1876.             move(scrlin, 0);
  1877.             addch(artlin-1 == curind ? '>' :
  1878.                 (xflag || isunread(thisng[artlin-2].i_artnum) ? ' ': 'D'));
  1879.             sprintf(bfr, "%3d%5d%5d %-36s  %s",
  1880.                 artlin-1, thisng[artlin-2].i_artnum, thisng[artlin-2].i_nlines,
  1881.                 thisng[artlin-2].i_title, thisng[artlin-2].i_from);
  1882.             bfr[COLS] = '\0';
  1883.             addstr(bfr);
  1884.         }
  1885.     }
  1886. }
  1887.  
  1888.  
  1889. /*
  1890.  * Dump the help message to the screen.  The help msg can't be scrolled.
  1891.  */
  1892. dumphelp(artlin, scrlin, count) {
  1893.     FILE *helpf;
  1894. #ifndef VHELP
  1895.     char VHELP[FPATHLEN];
  1896. #endif
  1897.  
  1898. #ifndef VHELP
  1899.     sprintf(VHELP, "%s/vnews.help", LIB);
  1900. #endif
  1901.     if ((helpf = fopen(VHELP, "r")) == NULL) {
  1902.         addstr("Can't open help file");
  1903.         return;
  1904.     }
  1905.     while (fgets(bfr, LBUFLEN, helpf) != NULL) {
  1906.         nstrip(bfr);
  1907.         mvaddstr(scrlin, 0, bfr);
  1908.         scrlin++;
  1909.     }
  1910.     fclose(helpf);
  1911. }
  1912.  
  1913.  
  1914. /*
  1915.  * Dump the full header on the screen.  The header cannot be scrolled.
  1916.  * We use the routine appfile to append to the file, which forces us
  1917.  * to do some juggling to return to the article.
  1918.  */
  1919. dumphdr(artlin, scrlin, count) {
  1920.     long saveoff;
  1921.     int svartread, svartlines, svlastlin;
  1922.     int stoppos = scrlin + count - 1 ;
  1923.     register int i;
  1924.  
  1925.     saveoff = ftell(fp);
  1926.     fseek(fp, 0L, 0);
  1927.     svartread = artread, svartlines = artlines, svlastlin = lastlin;
  1928.     artread = 0;
  1929.     appfile(fp, artlines + count);
  1930.     if (count > lastlin - svlastlin)
  1931.         count = lastlin - svlastlin;
  1932.     for (i = 0 ; i < count ; i++) {
  1933.         tfget(bfr, svlastlin + i);
  1934.         if (bfr[0] == '\0')
  1935.             break;
  1936.         mvaddstr(scrlin + i, 0, bfr);
  1937.     }
  1938.     nochange(i + 1, stoppos) ;
  1939.     artread = svartread, artlines = svartlines, lastlin = svlastlin;
  1940.     fseek(fp, saveoff, 0);
  1941. }
  1942.  
  1943.  
  1944. nullsub() {;}
  1945. !E!O!F!
  1946.  
  1947. cat > vnews/genmakefile <<\!E!O!F!
  1948. : This shell procedure generates the vnews makefile.
  1949.  
  1950. LIB=../lib
  1951. if test ! -f $LIB/makedefs
  1952. then    echo "$LIB/makedefs not does not exist"
  1953.     exit 1
  1954. fi
  1955. exec > temp
  1956. . $LIB/makedefs
  1957. cat <<!
  1958. # Vnews makefile 2.11-B 1/18/85
  1959.  
  1960. # definitions
  1961.  
  1962. !
  1963. cat $LIB/makedefs
  1964. sed -e 's:$LIB:'$LIB:g <<\!
  1965. LOGDIR =
  1966.  
  1967. IBMFLAGS =
  1968. DEBUG = -O
  1969. CFLAGS = $(DEBUG) $(IBMFLAGS) -I$LIB
  1970. LFLAGS = -i $(DEBUG) $(IBMFLAGS)
  1971.  
  1972. VOBJECTS = readnews.o vextern.o $(LOGDIR) vreadr.o dispcntl.o artseq.o termio.o virtterm.o
  1973.  
  1974. # dependencies
  1975.  
  1976. all: makefile vnews
  1977.  
  1978. makefile: genmakefile $LIB/makedefs
  1979.     genmakefile
  1980.     @echo 'Makefile changed, so restart make.'
  1981.     @sh -c 'exit 22'
  1982.  
  1983. install: all
  1984.     -/bin/mv $(BINDIR)/vnews $(BINDIR)/ovnews
  1985.     /bin/cp vnews $(BINDIR)/vnews
  1986.     -/bin/mv $(LIBDIR)/vnews.help $(LIBDIR)/ovnews.help
  1987.     /bin/cp vnews.help $(LIBDIR)/vnews.help
  1988.  
  1989. clean:
  1990.     rm -f core *.o
  1991.  
  1992. readnews.o:  readnews.c rparams.h
  1993.     $(CC) $(CFLAGS) -c readnews.c
  1994.  
  1995. rfuncs.o:  rfuncs.c rparams.h
  1996.     $(CC) $(CFLAGS) -c rfuncs.c
  1997.  
  1998. logdir.o: logdir.c
  1999.     $(CC) $(CFLAGS) -c logdir.c
  2000.  
  2001. vnews:    $(VOBJECTS) $LIB/rpathinit.o $LIB/rlib.a
  2002.     $(CC) $(LFLAGS) $(VOBJECTS) $LIB/rpathinit.o $LIB/rlib.a $(TERMCAP) -o $@
  2003.  
  2004. vreadr.o dispcntl.o artseq.o termio.o: vnews.h rparams.h
  2005.  
  2006. vextern.o:  vextern.c rparams.h
  2007.  
  2008. VNEWSLINT = artseq.c dispcntl.c readnews.c vextern.c rfuncs.c\
  2009. termio.c virtterm.c vreadr.c\
  2010. $LIB/addrc.c $LIB/afopen.c $LIB/allgroups.c $LIB/bcopy.s\
  2011. $LIB/bfr.c $LIB/bzero.c $LIB/cancel.c $LIB/ckfopen.c\
  2012. $LIB/ckmalloc.c $LIB/dirname.c $LIB/findgroup.c\
  2013. $LIB/genafopen.c $LIB/gethead.c $LIB/getuser.c $LIB/gfopen.c\
  2014. $LIB/hash.c $LIB/hfgets.c $LIB/isadmin.c $LIB/lookup.c\
  2015. $LIB/makehimask.c $LIB/mypathinit.c $LIB/nextgrp.c\
  2016. $LIB/ngchain.c $LIB/ngmatch.c $LIB/nsavestr.c $LIB/nstrip.c\
  2017. $LIB/openrc.c $LIB/pgetuser.c $LIB/prefix.c $LIB/prevgrp.c\
  2018. $LIB/process.c $LIB/read.c $LIB/readinrc.c $LIB/rename.c\
  2019. $LIB/roptions.c $LIB/savestr.c $LIB/scopyn.c $LIB/setupgrp.c\
  2020. $LIB/titmat.c
  2021.  
  2022. vnewslint:
  2023.     lint -I$LIB -DSPOOLDIR=\"$(SPOOLDIR) -DLIBDIR=\"$(LIBDIR) $(VNEWSLINT)        # You must be masochistic
  2024. !
  2025.  
  2026. mv temp makefile
  2027. !E!O!F!
  2028. chmod +x vnews/genmakefile
  2029.  
  2030. cat > vnews/readnews.c <<\!E!O!F!
  2031. /*
  2032.  * readnews - read news articles.
  2033.  */
  2034.  
  2035. /* static char    *SccsId = "%W%    %G%"; */
  2036.  
  2037. #include "rparams.h"
  2038. #include "artfile.h"
  2039. #include "ng.h"
  2040.  
  2041. /*
  2042.  * readnews - article reading program
  2043.  */
  2044.  
  2045.  
  2046. /*
  2047.  *    Authors:
  2048.  *        Matt Glickman    ucbvax!glickman
  2049.  *        Mark Horton    cbosg!mark
  2050.  *        Stephen Daniels    duke!swd
  2051.  *        Tom Truscott    duke!trt
  2052.  */
  2053.  
  2054. main(argc, argv)
  2055. int    argc;
  2056. register char    **argv;
  2057. {
  2058.     FILE *rcfp;
  2059.     FILE *openrc();
  2060.     time_t convdate() ;
  2061.  
  2062.     /* set up defaults and initialize. */
  2063.     pathinit();
  2064.  
  2065. #ifndef SHELL
  2066.     if ((SHELL = getenv("SHELL")) == NULL)
  2067.         SHELL = "/bin/sh";
  2068. #endif
  2069.     getuser();
  2070.  
  2071.     rcfp = openrc();
  2072.     roptions(argv, rcfp);
  2073.  
  2074.     if (datebuf) {
  2075.         atime = convdate(datebuf) ;
  2076.         free(datebuf) ;
  2077.     }
  2078.  
  2079.     /*
  2080.      * ALL of the command line has now been processed. (!)
  2081.      */
  2082.  
  2083.     if (sflag) {
  2084.         printf("Subscription list:  %s\n", sublist);
  2085.         xxit(0);
  2086.     }
  2087.  
  2088.     afopen();
  2089.     if (xflag)
  2090.         readinrc((FILE *)NULL);
  2091.     else {
  2092.         fseek(rcfp, 0L, 0);
  2093.         readinrc(rcfp);
  2094.     }
  2095.     fclose(rcfp);
  2096.  
  2097. #ifdef DEBUG
  2098.     fprintf(stderr, "sublist = %s\n", sublist);
  2099. #endif
  2100.  
  2101.     readr();
  2102.  
  2103.     fflush(stdout);
  2104.     if (xflag || lflag || tflag)
  2105.         xxit(0);
  2106.     writeoutrc();
  2107.     xxit(0);
  2108.  
  2109.     /* Camel, R.O.H. */
  2110. }
  2111.  
  2112.  
  2113.  
  2114. /*
  2115.  * convert a date to UNIX internal format.
  2116.  */
  2117.  
  2118. time_t
  2119. convdate(s)
  2120.       char *s ;
  2121.       {
  2122.       char buf[512] ;
  2123.       FILE *datefp, *popen() ;
  2124.       long cgtdate() ;
  2125.       long atol() ;
  2126.  
  2127.       sprintf(buf, "%s/cgtdate '%s'", LIB, s) ;
  2128.       if ((datefp = popen(buf, "r")) == NULL || fgets(buf, 512, datefp) == NULL)
  2129.             xerror("Can't convert -a date") ;
  2130.       return atol(buf) ;
  2131. }
  2132. !E!O!F!
  2133.  
  2134. cat > vnews/rfuncs.c <<\!E!O!F!
  2135. /*
  2136.  * rfuncs - functions for readnews.
  2137.  */
  2138.  
  2139. static char    *SccsId = "@(#)rfuncs.c    2.9    3/7/83";
  2140.  
  2141. #include "rparams.h"
  2142. #include "newsrc.h"
  2143.  
  2144. #ifdef notdef
  2145. /*
  2146.  * Figure out the number of the largest article in newsgroup ng,
  2147.  * and return that value.
  2148.  */
  2149. long
  2150. findngsize(ng)
  2151. char *ng;
  2152. {
  2153.     FILE *af;
  2154.     long s;
  2155.     char buf[100], n[100];
  2156.  
  2157.     af = xfopen(ACTIVE, "r");
  2158.     while (fgets(buf, sizeof buf, af)) {
  2159.         sscanf(buf, "%s %ld", n, &s);
  2160.         if (strcmp(n, ng) == 0) {
  2161.             fclose(af);
  2162.             return s;
  2163.         }
  2164.     }
  2165.     return 0;
  2166. }
  2167. #endif
  2168.  
  2169.  
  2170.  
  2171. xxit(status)
  2172. int    status;
  2173. {
  2174.     exit(status);
  2175. }
  2176.  
  2177.  
  2178. /*
  2179.  * Return true if the newsgroup was specified in the -n option.
  2180.  */
  2181.  
  2182. wewant(name)
  2183.     char *name;
  2184.     {
  2185.     return ngmatch(name, sublist);
  2186. }
  2187. !E!O!F!
  2188.  
  2189. cat > vnews/rparams.h <<\!E!O!F!
  2190. /*
  2191.  * rparams.h - parameters for readnews, rfuncs, and readr.
  2192.  */
  2193.  
  2194. /* static char *Rparams = "@(#)rparams.h    2.8    5/28/83"; */
  2195.  
  2196. #include "config.h"
  2197. #include <stdio.h>
  2198. #include <signal.h>
  2199. #include <sys/types.h>
  2200. #include <sys/stat.h>
  2201. #include <ctype.h>
  2202. #ifdef SIGXCPU
  2203. /* 4.2BSD moved this file, grumble grumble */
  2204. #include <sys/time.h>
  2205. #else
  2206. #include <time.h>
  2207. #endif
  2208. #include "defs.h"
  2209. #include "arthead.h"
  2210. #include "libextern.h"
  2211. #include "roptions.h"
  2212.  
  2213. #define    NEXT    0
  2214. #define SPEC    1
  2215.  
  2216. #define    FORWARD    0
  2217. #define BACKWARD 1
  2218.  
  2219. /* external declarations stolen from params.h */
  2220. #ifndef SHELL
  2221. extern char    *SHELL;
  2222. #endif
  2223.  
  2224. /* external function declarations */
  2225. extern    char    *strcpy(), *strncpy(), *strcat(), *index(), *rindex();
  2226. extern    char    *ctime(), *mktemp(), *malloc(), *realloc(), *getenv();
  2227. extern    char    *arpadate(), *dirname();
  2228. extern    time_t    time(), getdate(), cgtdate();
  2229. extern    int    broadcast(), save(), newssave(), ushell(), pshell(), onsig();
  2230. extern    long    atol();
  2231.  
  2232. char *savestr();
  2233.  
  2234. /* external declarations specific to readnews */
  2235. extern int    mode;
  2236. extern time_t    atime;
  2237. !E!O!F!
  2238.  
  2239. cat > vnews/termio.c <<\!E!O!F!
  2240. #include "vnews.h"
  2241. #if USGREL >= 30
  2242. #include <termio.h>
  2243. #include <fcntl.h>
  2244. #else
  2245. #include <sgtty.h>
  2246. #endif
  2247. #include <errno.h>
  2248. extern int errno;
  2249. static int alflag;            /* set if unprocessed alarm signal */
  2250. static int intflag;            /* set if interrupt received */
  2251. #ifdef SIGTSTP
  2252. static int inraw;            /* true if in raw mode */
  2253. #endif
  2254. int onstop();
  2255.  
  2256.  
  2257. /*** Terminal I/O ***/
  2258.  
  2259. #define INBUFSIZ 8
  2260.  
  2261. char inbuf[INBUFSIZ];            /* input buffer */
  2262. char outbuf[BUFSIZ];            /* output buffer */
  2263. int innleft = 0;            /* # of chars in input buffer */
  2264. int outnleft = BUFSIZ;            /* room left in output buffer */
  2265. char *innext;                /* next input character */
  2266. char *outnext = outbuf;            /* next space in output buffer */
  2267. #ifndef CURSES
  2268. #if USGREL >= 30
  2269. int oflags;                /* fcntl flags (for nodelay read) */
  2270. #endif
  2271. #endif
  2272.  
  2273.  
  2274. /*
  2275.  * Input a character
  2276.  */
  2277.  
  2278. vgetc() {
  2279.     register c;
  2280. #if BSDREL >= 42
  2281.     int readfds, exceptfds;
  2282. #endif
  2283.  
  2284. recurse:
  2285.     if (--innleft >= 0) {
  2286.         c = *innext++;
  2287.     } else {
  2288.         if (alflag)
  2289.             timer();
  2290.         updscr();    /* update the display */
  2291.         for (;;) {
  2292.             if (innleft > 0 || alflag)
  2293.                 goto recurse;
  2294.             intflag = 0;
  2295. #ifndef CURSES
  2296. #if USGREL >= 30
  2297.             if (oflags & O_NDELAY) {
  2298.                 oflags &=~ O_NDELAY;
  2299.                 fcntl(0, F_SETFL, oflags);
  2300.             }
  2301. #endif
  2302. #endif
  2303. #if BSDREL >= 42
  2304.             /* Use a select because can be interrupted */
  2305.             readfds = 1; exceptfds = 1;
  2306.             innleft = select(1, &readfds, 0, &exceptfds, 0);
  2307.             if (innleft > 0) {
  2308.                 if ((innleft = read(0, inbuf, INBUFSIZ)) > 0)
  2309.                     break;
  2310.             }
  2311. #else
  2312.             if ((innleft = read(0, inbuf, INBUFSIZ)) > 0)
  2313.                 break;
  2314. #endif
  2315.             if (hupflag)
  2316.                 return INTR;
  2317.             if (innleft == 0 || errno != EINTR)
  2318.                 abort();    /* "Can't happen" */
  2319.             if (intflag) {
  2320.                 intflag--;
  2321.                 return INTR;
  2322.             }
  2323.         }
  2324.         innext = inbuf + 1;
  2325.         innleft--;
  2326.         c = inbuf[0];
  2327.     }
  2328. #ifdef V6
  2329.     c &= 0177;
  2330.     if (c == '\034')    /* FS character */
  2331.         onquit();
  2332.     if (c == '\177')    /* DEL */
  2333.         onint();
  2334. #endif
  2335.     if (c == '\f') {
  2336.         clearok(curscr, 1);
  2337.         goto recurse;
  2338.     }
  2339.     if (c == '\r')
  2340.         c = '\n';
  2341.     return c;
  2342. }
  2343.  
  2344.  
  2345. /*
  2346.  * Push a character back onto the input stream.
  2347.  */
  2348.  
  2349. pushback(c) {
  2350.     if (innext <= inbuf)
  2351.         abort();
  2352.     *--innext = c;
  2353.     innleft++;
  2354. }
  2355.  
  2356.  
  2357.  
  2358. /*
  2359.  * Check for terminal input
  2360.  */
  2361.  
  2362. checkin() {
  2363. #ifndef CURSES
  2364. #if BSDREL > 7
  2365.     long count;
  2366.  
  2367. #endif
  2368. #endif
  2369. #ifdef STATTOP
  2370.     if (innleft > 0)
  2371. #else
  2372.     if (innleft > 0 || alflag)
  2373. #endif
  2374.         return 1;
  2375. #if !defined(CURSES) && (USGREL >= 30 || BSDREL > 7)
  2376.     if (ospeed == B9600)
  2377.         return 0;
  2378.     vflush();
  2379.     if (ospeed <= B300)
  2380.         ttyowait();
  2381. #if USGREL >= 30
  2382.     if ((oflags & O_NDELAY) == 0) {
  2383.         oflags |= O_NDELAY;
  2384.         fcntl(0, F_SETFL, oflags);
  2385.     }
  2386.     if ((innleft = read(0, inbuf, INBUFSIZ)) > 0) {
  2387.         innext = inbuf;
  2388.         return 1;
  2389.     }
  2390. #else
  2391.     count = 0;            /* in case FIONREAD fails */
  2392.     ioctl(0, FIONREAD, &count);
  2393.     if (count)
  2394.         return 1;
  2395. #endif
  2396. #endif
  2397.     return 0;
  2398. }
  2399.  
  2400.  
  2401.  
  2402. /*
  2403.  * flush terminal input queue.
  2404.  */
  2405.  
  2406. clearin() {
  2407. #if USGREL >= 30
  2408.     ioctl(0, TCFLSH, 0);
  2409. #else
  2410. #ifdef TIOCFLUSH
  2411.     ioctl(0, TIOCFLUSH, 0);
  2412. #else
  2413.     struct sgttyb tty;
  2414.     gtty(0, &tty);
  2415.     stty(0, &tty);
  2416. #endif
  2417. #endif
  2418.     innleft = 0;
  2419. }
  2420.  
  2421.  
  2422. vputc(c) {
  2423.     if (--outnleft < 0) {
  2424.         vflush();
  2425.         outnleft--;
  2426.     }
  2427.     *outnext++ = c;
  2428. }
  2429.  
  2430.  
  2431. /*
  2432.  * Flush the output buffer
  2433.  */
  2434.  
  2435. vflush() {
  2436.     register char *p;
  2437.     register int i;
  2438. #if BSDREL <= 7 && USGREL < 30
  2439.     int svalarm = alarm(0);        /* signals cause UNIX to drop characters */
  2440. #endif
  2441.  
  2442.     for (p = outbuf ; p < outnext ; p += i) {
  2443.         if (hupflag)
  2444.             break;
  2445.         if ((i = write(1, p, outnext - p)) < 0) {
  2446.             if (errno != EINTR)
  2447.                 abort();    /* "Can't happen" */
  2448.             i = 0;
  2449.         }
  2450.     }
  2451.     outnleft = BUFSIZ;
  2452.     outnext = outbuf;
  2453. #if BSDREL <= 7 && USGREL < 30
  2454.     alarm(svalarm);
  2455. #endif
  2456. }
  2457.  
  2458.  
  2459.  
  2460.  
  2461. /*** terminal modes ***/
  2462.  
  2463. #ifndef CURSES
  2464. #if USGREL >= 30
  2465. static struct termio oldtty, newtty;
  2466.  
  2467.  
  2468. /*
  2469.  * Save tty modes
  2470.  */
  2471.  
  2472. ttysave() {
  2473.     if (ioctl(1, TCGETA, &oldtty) < 0)
  2474.         xerror("Can't get tty modes");
  2475.     newtty = oldtty;
  2476.     newtty.c_iflag &=~ (INLCR|IGNCR|ICRNL);
  2477.     newtty.c_oflag &=~ (OPOST);
  2478.     newtty.c_lflag &=~ (ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  2479.     newtty.c_lflag |=  (NOFLSH);
  2480.     newtty.c_cc[VMIN] = 1;
  2481.     newtty.c_cc[VTIME] = 0;
  2482.     cerase = oldtty.c_cc[VERASE];
  2483.     ckill = oldtty.c_cc[VKILL];
  2484.     ospeed = oldtty.c_cflag & CBAUD;
  2485.     initterm();
  2486. }
  2487.  
  2488.  
  2489. /*
  2490.  * Set tty modes for visual processing
  2491.  */
  2492.  
  2493. ttyraw() {
  2494.     while (ioctl(1, TCSETAF, &newtty) < 0 && errno == EINTR);
  2495.     rawterm();
  2496. }
  2497.  
  2498.  
  2499.  
  2500. ttyowait() {    /* wait for output queue to drain */
  2501.     while (ioctl(1, TCSETAW, &newtty) < 0 && errno == EINTR);
  2502. }
  2503.  
  2504.  
  2505. /*
  2506.  * Restore tty modes
  2507.  */
  2508.  
  2509. ttycooked() {
  2510.     cookedterm();
  2511.     while (ioctl(1, TCSETAF, &oldtty) < 0 && errno == EINTR);
  2512.     oflags &=~ O_NDELAY;
  2513.     fcntl(0, F_SETFL, oflags) ;
  2514. }
  2515.  
  2516. #else
  2517.  
  2518. static struct sgttyb oldtty, newtty;
  2519. #if BSDREL >= 40 && BSDREL <= 41
  2520. static struct tchars oldtchars, newtchars;
  2521. #endif
  2522.  
  2523.  
  2524. /*
  2525.  * Save tty modes
  2526.  */
  2527.  
  2528. ttysave() {
  2529. #ifdef SIGTSTP
  2530.     /* How to get/change terminal modes in a job control environment.
  2531.        This code is right from the 4.1 bsd jobs(3) manual page.
  2532.      */
  2533. #if BSDREL >= 42
  2534.     int tpgrp, getpgrp();
  2535. #else
  2536.     short tpgrp, getpgrp();
  2537. #endif
  2538.  
  2539. retry:
  2540. #if BSDREL >= 42
  2541.     sigblock(1<<(SIGTSTP-1) | 1<<(SIGTTIN-1) | 1<<(SIGTTOU-1));
  2542. #else
  2543.     sigset(SIGTSTP, SIG_HOLD);
  2544.     sigset(SIGTTIN, SIG_HOLD);
  2545.     sigset(SIGTTOU, SIG_HOLD);
  2546. #endif
  2547.     if (ioctl(2, TIOCGPGRP, &tpgrp) != 0)
  2548.         goto nottty;
  2549.     if (tpgrp != getpgrp(0)) { /* not in foreground */
  2550.         sigset(SIGTTOU, SIG_DFL);
  2551. #if BSDREL >= 42
  2552.         sigsetmask(sigblock(0) & ~(1<<(SIGTTOU-1)));
  2553. #endif
  2554.         kill(0, SIGTTOU);
  2555.         /* job stops here waiting for SIGCONT */
  2556.         goto retry;
  2557.     }
  2558.     sigset(SIGTTIN, onstop);
  2559.     sigset(SIGTTOU, onstop);
  2560.     sigset(SIGTSTP, onstop);
  2561. #if BSDREL >= 42
  2562.     sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1) | 1<<(SIGTTIN-1) | 1<<(SIGTTOU-0)));
  2563. #endif
  2564. #endif
  2565.     if (gtty(1, &oldtty) < 0)
  2566. nottty:        xerror("Can't get tty modes");
  2567.     newtty = oldtty;
  2568.     newtty.sg_flags &=~ (CRMOD|ECHO|XTABS);
  2569. #if BSDREL >= 7
  2570.     newtty.sg_flags |= CBREAK;
  2571. #else
  2572.     newtty.sg_flags |= RAW;
  2573. #endif
  2574.     cerase = oldtty.sg_erase;
  2575.     ckill = oldtty.sg_kill;
  2576.     ospeed = oldtty.sg_ospeed;
  2577. #if BSDREL >= 40 && BSDREL <= 41
  2578.     ioctl(1, TIOCGETC, (char *)&oldtchars);
  2579.     newtchars = oldtchars;
  2580.     if (oldtchars.t_intrc == '\7')
  2581.         newtchars.t_intrc = -1;
  2582. #endif
  2583.     initterm();
  2584. }
  2585.  
  2586.  
  2587. /*
  2588.  * Set tty modes for visual processing
  2589.  */
  2590.  
  2591. ttyraw() {
  2592. #ifdef SIGTSTP
  2593.     inraw = 1;
  2594. #endif
  2595.     while (stty(1, &newtty) < 0 && errno == EINTR);
  2596. #if BSDREL >= 40 && BSDREL <= 41
  2597.     ioctl(1, TIOCSETC, (char *) &newtchars);
  2598. #endif
  2599.     rawterm();
  2600. }
  2601.  
  2602.  
  2603.  
  2604. ttyowait() {    /* wait for output queue to drain */
  2605. #ifdef TIOCDRAIN    /* This ioctl is a local mod on linus */
  2606.     ioctl(1, TIOCDRAIN, 0);
  2607. #endif
  2608. }
  2609.  
  2610.  
  2611. /*
  2612.  * Restore tty modes
  2613.  */
  2614.  
  2615. ttycooked() {
  2616.     cookedterm();
  2617.     while (stty(1, &oldtty) < 0 && errno == EINTR);
  2618. #if BSDREL >= 40 && BSDREL <= 41
  2619.     ioctl(1, TIOCSETC, (char *) &oldtchars);
  2620. #endif
  2621. #ifdef SIGTSTP
  2622.     inraw = 0;
  2623. #endif
  2624. }
  2625.  
  2626. #endif
  2627. #else
  2628. #ifdef HCURSES
  2629.  
  2630. ttysave() {
  2631.     initscr();
  2632.     idlok(stdscr, 1);
  2633.     intrflush(curscr, 0);
  2634.     cerase = erasechar();
  2635.     ckill = killchar();
  2636. }
  2637.  
  2638.  
  2639. ttyraw() {
  2640.     reset_prog_mode();
  2641.     cbreak();
  2642.     nonl();
  2643.     noecho();
  2644. }
  2645.  
  2646.  
  2647. ttycooked() {
  2648.     reset_shell_mode();
  2649. }
  2650.  
  2651.  
  2652. int _endwin;        /* [expletives deleted] */
  2653.  
  2654. #else
  2655.  
  2656. ttysave() {
  2657. #if USGREL >= 30
  2658.     struct termio tty;
  2659. #else
  2660.     struct sgttyb tty;
  2661. #endif
  2662.  
  2663.     initscr();
  2664. #if USGREL >= 30
  2665.     ioctl(0, TCGETA, &tty);
  2666.     cerase = tty.c_cc[VERASE];
  2667.     ckill = tty.c_cc[VKILL];
  2668. #else
  2669.     gtty(0, &tty);
  2670.     cerase = tty.sg_erase;
  2671.     ckill = tty.sg_kill;
  2672. #endif
  2673. }
  2674.  
  2675.  
  2676. ttyraw() {
  2677.     /*
  2678.      * Vnews really isn't designed to work with RAW mode, so if you
  2679.      * have anything approaching CBREAK mode, use it.
  2680.      */
  2681. #ifdef CBREAK
  2682.     crmode();
  2683. #else
  2684.     raw();
  2685. #endif
  2686.     nonl();
  2687.     noecho();
  2688. }
  2689.  
  2690.  
  2691. ttycooked() {
  2692.     resetty();
  2693. }
  2694.  
  2695. #endif
  2696. #endif CURSES
  2697.  
  2698.  
  2699.  
  2700. /*** signal handlers ***/
  2701.  
  2702. onint() {
  2703.     signal(SIGINT, onint);
  2704.     clearin();            /* flush input queue */
  2705. #if BSDREL >= 40 && BSDREL <= 41
  2706.     ioctl(0, TIOCSTI, "\7");
  2707. #else
  2708.     intflag++;
  2709. #endif
  2710. }
  2711.  
  2712.  
  2713. onhup() {
  2714.     signal(SIGHUP, onhup);
  2715.     hupflag++;
  2716. }
  2717.  
  2718.  
  2719. onquit() {
  2720.     botscreen();
  2721.     vflush();
  2722.     ttycooked();
  2723. #ifdef COREDUMP
  2724.     abort();
  2725. #else
  2726.     exit(0);
  2727. #endif
  2728. }
  2729.  
  2730. #ifdef SIGTSTP
  2731.  
  2732. onstop(signo)
  2733.     int signo;
  2734. {
  2735.     int restore = inraw;
  2736.     int e = errno;
  2737.  
  2738.     /* restore old terminal state */
  2739.     if (restore) {
  2740.         botscreen();
  2741.         vflush();
  2742.         ttycooked();
  2743.     }
  2744.     sigset(signo, SIG_DFL);
  2745. #if BSDREL >= 42
  2746.     sigsetmask(sigblock(0) & ~(1<<(signo-1)));
  2747. #endif
  2748.     kill(getpid(), signo);    /* stop here until continued */
  2749.  
  2750.     /* fprintf(stderr,"Vnews restarted."); */
  2751.     sigset(signo, onstop);
  2752.     /* restore our special terminal state */
  2753.     if (restore) {
  2754.         ttyraw();
  2755. #ifdef TIOCSTI
  2756.         ioctl(0, TIOCSTI, "\f");
  2757. #else
  2758.         clearok(curscr, 1);    /* doesn't seem to work */
  2759. #endif
  2760.     }
  2761.     errno = e;
  2762. }
  2763.  
  2764. #endif
  2765.  
  2766.  
  2767.  
  2768. /*** alarm handler ***/
  2769.  
  2770. /* #include <time.h> */
  2771.  
  2772.  
  2773. /*
  2774.  * Called on alarm signal.
  2775.  * Simply sets flag, signal processed later.
  2776.  */
  2777.  
  2778. onalarm() {
  2779.     alflag++;
  2780. }
  2781.  
  2782.  
  2783. /*
  2784.  * Process alarm signal (or start clock)
  2785.  */
  2786.  
  2787. timer() {
  2788.     long tod;
  2789.     int hour;
  2790.     int i;
  2791.     struct tm *t;
  2792.     struct stat statb;
  2793.     long time();
  2794.     static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
  2795.     static long oldmsize = 1000000L;
  2796.     static int rccount = 10;
  2797.  
  2798.     alflag = 0;
  2799.     signal(SIGALRM, onalarm);
  2800.     time(&tod);
  2801.     t = localtime(&tod);
  2802.     i = 60 - t->tm_sec;
  2803.     alarm(i > 30? 30 : i);            /* reset alarm */
  2804.     hour = t->tm_hour % 12;
  2805.     if (hour == 0)  hour = 12;
  2806.     sprintf(timestr, "%.3s %d %d:%02d",
  2807.         months + 3 * t->tm_mon, t->tm_mday, hour, t->tm_min);
  2808. #ifdef GGRMAIL
  2809.     if (mailf == NULL || stat(mailf, &statb) < 0) {
  2810.         statb.st_size = 0;
  2811.     }
  2812.     if(statb.st_size > oldmsize) {
  2813.         ismail = 1;
  2814.         needbeep++;
  2815.     } else if (statb.st_size < oldmsize) {
  2816.         ismail = 0;
  2817.     }
  2818.     oldmsize = statb.st_size;
  2819. #else
  2820.     ismail = 0;
  2821.     if (mailf != NULL && stat(mailf, &statb) >= 0 && statb.st_size > 0L) {
  2822.         ismail = 1;
  2823.         if (oldmsize < statb.st_size) {
  2824.             ismail = 2;        /* new mail */
  2825.             needbeep++;
  2826.         }
  2827.     } else {
  2828.         statb.st_size = 0L;
  2829.     }
  2830.     oldmsize = statb.st_size;
  2831. #endif
  2832.     if (uflag && --rccount < 0) {
  2833.         writeoutrc();
  2834.         if (secpr[0] == '\0')
  2835.             strcpy(secpr, ".newsrc updated");
  2836.         rccount = 10;
  2837.     }
  2838. }
  2839. !E!O!F!
  2840.  
  2841. cat > vnews/vextern.c <<\!E!O!F!
  2842. /*
  2843.  * rextern - external definitions for readnews
  2844.  */
  2845.  
  2846. /* static char    *SccsId = "@(#)rextern.c    2.5    3/30/83"; */
  2847.  
  2848. #include "rparams.h"
  2849.  
  2850. time_t    atime;
  2851.  
  2852. #ifndef SHELL
  2853. char    *SHELL;
  2854. #endif
  2855. !E!O!F!
  2856.  
  2857. echo Part 6 of 7 extracted.
  2858.  
  2859.  
  2860.